package cn.yemuysy.code.lingting.core;

import cn.yemuysy.code.lingting.model.MatchResult;
import cn.yemuysy.code.lingting.model.SensitiveWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 组合敏感词匹配器
 * 实现组合敏感词的映射关系管理和完整性验证
 */
public class CombinationMatcher {
    
    private static final Logger logger = LoggerFactory.getLogger(CombinationMatcher.class);
    
    /**
     * 组合敏感词映射表
     * Key: 组合敏感词标识, Value: 组合敏感词对象
     */
    private Map<String, SensitiveWord> combinationWords;
    
    /**
     * 单个敏感词到组合的反向映射
     * Key: 单个敏感词, Value: 包含该词的所有组合敏感词列表
     */
    private Map<String, List<SensitiveWord>> partToCombinations;
    
    /**
     * AC自动机实例
     */
    private AhoCorasickAutomaton automaton;
    
    public CombinationMatcher() {
        this.combinationWords = new HashMap<>();
        this.partToCombinations = new HashMap<>();
        this.automaton = new AhoCorasickAutomaton(false); // 字符模式
    }
    
    /**
     * 添加组合敏感词
     * @param combination 组合敏感词对象
     */
    public void addCombination(SensitiveWord combination) {
        if (combination == null || !combination.isCombination()) {
            logger.warn("无效的组合敏感词: {}", combination);
            return;
        }
        
        String combinationId = combination.getWord();
        combinationWords.put(combinationId, combination);
        
        // 添加组成部分到AC自动机
        for (String part : combination.getParts()) {
            automaton.addWord(part);
            
            // 建立反向映射
            partToCombinations.computeIfAbsent(part, k -> new ArrayList<>()).add(combination);
        }
        
        logger.debug("添加组合敏感词: {} -> {}", combinationId, combination.getParts());
    }
    
    /**
     * 批量添加组合敏感词
     * @param combinations 组合敏感词列表
     */
    public void addCombinations(Collection<SensitiveWord> combinations) {
        if (combinations == null || combinations.isEmpty()) {
            return;
        }
        
        for (SensitiveWord combination : combinations) {
            addCombination(combination);
        }
        
        logger.info("批量添加组合敏感词完成，总计: {} 个", combinations.size());
    }
    
    /**
     * 从字符串解析并添加组合敏感词
     * @param combinationStr 组合字符串，格式如："澳门+博彩+网站"
     */
    public void addCombinationFromString(String combinationStr) {
        if (combinationStr == null || combinationStr.trim().isEmpty()) {
            return;
        }
        
        SensitiveWord combination = SensitiveWord.parseCombinationWord(combinationStr);
        addCombination(combination);
    }
    
    /**
     * 搜索文本中的组合敏感词
     * @param text 要搜索的文本
     * @return 匹配的组合敏感词结果列表
     */
    public List<MatchResult> searchCombinations(String text) {
        if (text == null || text.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 第一步：使用AC自动机找出所有单个敏感词
        List<MatchResult> singleMatches = automaton.search(text);
        
        if (singleMatches.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 第二步：根据映射关系检查组合匹配
        return checkCombinationMatches(singleMatches, text);
    }
    
    /**
     * 检查组合敏感词匹配
     * @param singleMatches 单个敏感词匹配结果
     * @param originalText 原始文本
     * @return 组合敏感词匹配结果
     */
    private List<MatchResult> checkCombinationMatches(List<MatchResult> singleMatches, String originalText) {
        List<MatchResult> combinationResults = new ArrayList<>();
        
        // 按组合敏感词分组统计匹配的组成部分
        Map<String, CombinationMatchInfo> combinationMatches = new HashMap<>();
        
        for (MatchResult singleMatch : singleMatches) {
            String matchedWord = singleMatch.getWord();
            
            // 查找包含此单词的所有组合
            List<SensitiveWord> relatedCombinations = partToCombinations.get(matchedWord);
            if (relatedCombinations != null) {
                for (SensitiveWord combination : relatedCombinations) {
                    String combinationId = combination.getWord();
                    
                    CombinationMatchInfo matchInfo = combinationMatches.computeIfAbsent(
                        combinationId, k -> new CombinationMatchInfo(combination)
                    );
                    
                    matchInfo.addMatch(matchedWord, singleMatch);
                }
            }
        }
        
        // 检查每个组合是否完全匹配
        for (CombinationMatchInfo matchInfo : combinationMatches.values()) {
            if (matchInfo.isFullyMatched()) {
                MatchResult combinationResult = createCombinationResult(matchInfo, originalText);
                combinationResults.add(combinationResult);
                
                logger.debug("匹配到组合敏感词: {} -> {}", 
                           matchInfo.getCombination().getWord(), 
                           matchInfo.getMatchedParts());
            }
        }
        
        return combinationResults;
    }
    
    /**
     * 创建组合敏感词匹配结果
     */
    private MatchResult createCombinationResult(CombinationMatchInfo matchInfo, String text) {
        SensitiveWord combination = matchInfo.getCombination();
        List<MatchResult> partMatches = matchInfo.getPartMatches();
        
        // 计算组合匹配的起始和结束位置
        int startIndex = partMatches.stream().mapToInt(MatchResult::getStartIndex).min().orElse(0);
        int endIndex = partMatches.stream().mapToInt(MatchResult::getEndIndex).max().orElse(text.length() - 1);
        
        MatchResult result = new MatchResult(combination.getWord(), startIndex, endIndex, 
                                           MatchResult.MatchType.COMBINATION);
        
        // 添加组合的组成部分信息
        for (String part : matchInfo.getMatchedParts()) {
            result.addCombinationPart(part);
        }
        
        return result;
    }
    
    /**
     * 检查文本是否包含组合敏感词
     * @param text 要检查的文本
     * @return true如果包含组合敏感词
     */
    public boolean containsCombination(String text) {
        List<MatchResult> results = searchCombinations(text);
        return !results.isEmpty();
    }
    
    /**
     * 获取指定组合敏感词
     * @param combinationId 组合标识
     * @return 组合敏感词对象，不存在时返回null
     */
    public SensitiveWord getCombination(String combinationId) {
        return combinationWords.get(combinationId);
    }
    
    /**
     * 移除组合敏感词
     * @param combinationId 组合标识
     * @return 是否成功移除
     */
    public boolean removeCombination(String combinationId) {
        SensitiveWord combination = combinationWords.remove(combinationId);
        if (combination != null) {
            // 更新反向映射
            for (String part : combination.getParts()) {
                List<SensitiveWord> combinations = partToCombinations.get(part);
                if (combinations != null) {
                    combinations.remove(combination);
                    if (combinations.isEmpty()) {
                        partToCombinations.remove(part);
                    }
                }
            }
            
            // 重建AC自动机
            rebuildAutomaton();
            
            logger.debug("移除组合敏感词: {}", combinationId);
            return true;
        }
        return false;
    }
    
    /**
     * 重建AC自动机
     */
    private void rebuildAutomaton() {
        automaton.clear();
        Set<String> allParts = new HashSet<>();
        
        for (SensitiveWord combination : combinationWords.values()) {
            allParts.addAll(combination.getParts());
        }
        
        automaton.addWords(allParts);
    }
    
    /**
     * 获取组合敏感词数量
     */
    public int getCombinationCount() {
        return combinationWords.size();
    }
    
    /**
     * 获取所有组合敏感词
     */
    public Collection<SensitiveWord> getAllCombinations() {
        return new ArrayList<>(combinationWords.values());
    }
    
    /**
     * 清空所有组合敏感词
     */
    public void clear() {
        combinationWords.clear();
        partToCombinations.clear();
        automaton.clear();
        logger.info("组合敏感词匹配器已清空");
    }
    
    /**
     * 组合匹配信息辅助类
     */
    private static class CombinationMatchInfo {
        private SensitiveWord combination;
        private Set<String> matchedParts;
        private List<MatchResult> partMatches;
        
        public CombinationMatchInfo(SensitiveWord combination) {
            this.combination = combination;
            this.matchedParts = new HashSet<>();
            this.partMatches = new ArrayList<>();
        }
        
        public void addMatch(String part, MatchResult match) {
            matchedParts.add(part);
            partMatches.add(match);
        }
        
        public boolean isFullyMatched() {
            return combination.isFullyMatched(new ArrayList<>(matchedParts));
        }
        
        public SensitiveWord getCombination() {
            return combination;
        }
        
        public Set<String> getMatchedParts() {
            return matchedParts;
        }
        
        public List<MatchResult> getPartMatches() {
            return partMatches;
        }
    }
    
    @Override
    public String toString() {
        return "CombinationMatcher{" +
                "combinationCount=" + combinationWords.size() +
                ", totalParts=" + partToCombinations.size() +
                ", automatonWords=" + automaton.getWordCount() +
                '}';
    }
} 